package obecto.task.container
{
	import obecto.event.EventDescriptor;
	import obecto.event.TaskContainerEvent;
	import obecto.event.TaskEvent;
	import obecto.task.BaseTask;
	import obecto.task.ITask;
	import obecto.utility.Listener;

	[DefaultProperty("tasks")]
	public class Sequence extends BaseTask implements ITaskContainer, IMxmlTaskContainer
	{
		protected var _tasks : Array;
		protected var _runningTaskIndex : int;
		protected var runningTaskCompleteListener : Listener;
		
		public function Sequence(aTasks : Array = null)
		{
			super();

			_runningTaskIndex = -1;
			tasks = aTasks;
		}

		public function addTaskAt(task : ITask, index : int) : void
		{
			assertObjectIsNotNull(task);
			assertIndexInBounds(index);
			
			if(index <= _runningTaskIndex)
			{
				_runningTaskIndex++;
			}
			
			_tasks.splice(index, 0, task);
			
			dispatchEvent(new TaskContainerEvent(TaskContainerEvent.TASK_ADDED, task, null));
		}
		
		public function removeTaskAt(index : int) : void
		{
			assertIndexInBounds(index);
			
			var runningTaskChanged : Boolean = false;
			
			var task : ITask = ITask(_tasks.splice(index, 1)[0]);
			
			if(index == _runningTaskIndex)
			{
				runningTaskChanged = true;
				removeRunningTaskCompleteListener();
				_runningTaskIndex = getRunningTaskIndexAfterDeletion();
			}
			else if(index < _runningTaskIndex)
			{
				_runningTaskIndex--;
			}
			
			if(runningTaskChanged == true)
			{
				runNextTask();
			}
			
			dispatchEvent(new TaskContainerEvent(TaskContainerEvent.TASKS_REMOVED, task, null));
		}

		public function addTask(task : ITask) : void
		{
			addTaskAt(task, _tasks.length);
		}
		
		public function removeTask(task : ITask) : void
		{		
			assertObjectIsNotNull(task);
			
			var taskIndex : int = _tasks.indexOf(task);
			var runningTaskChanged : Boolean = false;
			
			while(taskIndex != -1)
			{
				_tasks.splice(taskIndex, 1);
				
				if(taskIndex == _runningTaskIndex)
				{
					runningTaskChanged = true;
					removeRunningTaskCompleteListener();
					_runningTaskIndex = getRunningTaskIndexAfterDeletion();
				}
				else if(taskIndex < _runningTaskIndex)
				{
					_runningTaskIndex--;	
				}
			}
			
			if(runningTaskChanged == true)
			{
				runNextTask();
			}
			
			dispatchEvent(new TaskContainerEvent(TaskContainerEvent.TASKS_REMOVED, task, null));
		}
		
		public function getAllTasks() : Array
		{
			return _tasks.slice();
		}
		
		override public function start() : void
		{
			if(running == false)
			{
				super.start();
				
				_runningTaskIndex = -1;
				runNextTask();
			}
		}
		
		protected function runNextTask() : void
		{
			_runningTaskIndex = getNextTaskIndex();
			
			if(_runningTaskIndex == getTerminatingIndex())
			{
				finish();
			}
			else
			{
				runningTaskCompleteListener = 
					Listener.createSingleTimeListener(tasks[_runningTaskIndex], TaskEvent.COMPLETE, handleRunningTaskComplete, true);
					
				runTask(tasks[_runningTaskIndex] as ITask);
			}
		}
		
		public function set tasks(value : Array) : void
		{
			if(_tasks == null || _tasks != value)
			{
				for each(var object : Object in value)
				{
					assertObjectIsTask(object);
				}
				
				if(running == true)
				{
					removeRunningTaskCompleteListener();
				}
				
				_tasks = (value != null) ? value : new Array();
				
				if(running == true)
				{
					_running = false;
					run();
				}
			}
		}
		
		public function get tasks() : Array
		{
			return _tasks;
		}
		
		public function get runningTaskIndex() : int
		{
			return _runningTaskIndex;
		}
		
		protected function removeRunningTaskCompleteListener() : void
		{
			if(runningTaskCompleteListener != null)
			{
				if(runningTaskCompleteListener.listening == true)
				{
					runningTaskCompleteListener.stop();
				}
				
				runningTaskCompleteListener = null;
			}
		}
		
		override protected function complete() : void
		{	
			_runningTaskIndex = -1;
			
			super.complete();
		}
		
		protected function handleRunningTaskComplete(event : TaskEvent) : void
		{
			removeRunningTaskCompleteListener();
			runNextTask();
		}
		
		protected function getRunningTaskIndexAfterDeletion() : int
		{
			return _runningTaskIndex - 1;
		}
		
		protected function getNextTaskIndex() : int
		{
			return _runningTaskIndex + 1;
		}
		
		protected function getTerminatingIndex() : int
		{
			return _tasks.length;
		}
		
		protected function run() : void
		{
			start();
		}
		
		protected function finish() : void
		{
			complete();
		}
		
		protected function runTask(task : ITask) : void
		{
			task.start();
		}
		
		protected function assertObjectIsNotNull(object : Object) : void
		{
			if(object == null)
			{
				throw new Error("Object can't be null.");
			}
		}
		
		protected function assertIndexInBounds(index : int) : void
		{
			if(index < 0 || index > _tasks.length)
			{
				throw new Error("Index out of bounds.");
			}			
		}
		
		protected function assertObjectIsTask(object : Object) : void
		{
			if((object is ITask) == false)
			{
				throw new Error("Object isn't task.");
			}				
		}		
	}
}